package main

import (
	"context"
	"fmt"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
	"math"
	"math/big"
	"strconv"
	"time"
	"v3-ethusdt/uni"
)

/**
*@Author: yepeng
*@Date: 2022/4/26 16:56
*@Name: router
*@Desc:
 */

//const MainNet = "https://mainnet.infura.io/v3/f648170fe1d7486d9a511470169ada84"

const MainNet = "wss://mainnet.infura.io/ws/v3/f648170fe1d7486d9a511470169ada84"
const UniV3USDCETH005 = "0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"

var db *gorm.DB

func main() {
	initDb()
	NewConnected()
}

type EthUsdc struct {
	gorm.Model
	NumberBloc uint64 `json:"number_bloc"`
	BlockHash  string `json:"block_hash"`
	Dire       string `json:"dire"`
	Price      string `json:"price"`
	Volume     string `json:"volume"`
	Value      string `json:"value"`
	Sender     string `json:"sender"`
	Recipient  string `json:"recipient"`
	TxIndex    uint   `json:"tx_index"`
	TxHash     string `json:"tx_hash"`
	Timestamp  uint64 `json:"timestamp"`
	DateTime   string `json:"date_time"`
}

func initDb() {
	dsn := "root:0112358@tcp(121.43.39.35:3306)/uniswapv3?charset=utf8mb4&parseTime=True&loc=Local"
	idb, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	if err != nil {
		log.Fatalf(" db error %s", err.Error())
	}
	db = idb

	err = db.AutoMigrate(&EthUsdc{})
	if err != nil {
		log.Fatalf("autp aigrate error:%s", err.Error())
	}
	// 查询数据库，判断区块高度是否同步
	ethusdc := EthUsdc{}
	result := db.Table("eth_usdcs").Limit(1).Find(&ethusdc)
	fmt.Println(result.RowsAffected)

}
func NewConnected() {
	client, err := ethclient.Dial(MainNet)
	defer client.Close()
	HandError(err, "eth client connect error")
	number, err := client.BlockNumber(context.Background())
	fmt.Println("block number", number)
	HandError(err, "get block error")
	contractAddress := common.HexToAddress(UniV3USDCETH005)
	smartContract, err := uni.NewUniswapV3Pool(contractAddress, client)
	HandError(err, "NewUniswapV3Pool error")
	// 查询数据库，判断区块高度是否同步
	var ethusdc EthUsdc
	result := db.Table("eth_usdcs").Limit(1).Find(&ethusdc)
	fmt.Println(result.RowsAffected)
	u := number
	if result.RowsAffected == 0 {
		u = number
	} else if ethusdc.NumberBloc == number {
		u = number
	} else {
		fmt.Println(" 未同步 u:", u, ethusdc.NumberBloc)
		u = ethusdc.NumberBloc + 1
	}
	fmt.Println("u:", u)
	start := new(uint64)
	start = &u
	opts := &bind.WatchOpts{
		Start:   start,
		Context: context.Background(),
	}

	ch := make(chan *uni.UniswapV3PoolSwap)
	sub, err := smartContract.WatchSwap(opts, ch, nil, nil)
	HandError(err, "watch swap error")
	for {
		select {
		case err := <-sub.Err():
			log.Fatal(err)
		case t := <-ch:
			handleSwapData(t, client)
		}
	}
}

// 处理swap event
func handleSwapData(t *uni.UniswapV3PoolSwap, client *ethclient.Client) {
	txHash := t.Raw.TxHash
	numberBlock := t.Raw.BlockNumber
	blocHash := t.Raw.BlockHash
	index := t.Raw.Index
	txIndex := t.Raw.TxIndex
	sender := t.Sender.String()
	recipient := t.Recipient.String()

	x, _ := strconv.ParseFloat(t.Amount0.String(), 64)
	y, _ := strconv.ParseFloat(t.Amount1.String(), 64)
	X := big.NewFloat(x)
	Y := big.NewFloat(y)

	volume := new(big.Float).Quo(Y, big.NewFloat(1000000000000000000))
	value := new(big.Float).Quo(X, big.NewFloat(1000000))
	price := new(big.Float).Quo(value, volume)
	f, _ := volume.Float64()
	//var direction = ""
	var dire = "buy"
	if f >= 0 {
		dire = "buy"
	} else {
		dire = "sell"
	}
	//fmt.Println(dire)
	volume.Abs(volume)
	value.Abs(value)
	price.Abs(price)
	priceS := FormatFloat(price, 4)
	volumeS := FormatFloat(volume, 4)
	valueS := FormatFloat(value, 4)

	fmt.Println("区块号:", numberBlock)
	fmt.Println("区块哈希", blocHash)
	fmt.Println("方向:", dire)
	fmt.Println("价格:", priceS)
	fmt.Println("数量:", volumeS)
	fmt.Println("市值:", valueS)
	fmt.Println("卖出者", sender)
	fmt.Println("买入者", recipient)
	fmt.Println("Index", index)
	fmt.Println("事务索引", txIndex)
	fmt.Println("事务哈希:", txHash)
	//fmt.Println("Tick:", tick)
	blockNumber := t.Raw.BlockNumber
	block, err := client.BlockByNumber(context.Background(), big.NewInt(int64(blockNumber)))
	if err != nil {
		log.Fatalf("error:%s", err.Error())
	}
	uTime := block.Time()
	blockT := time.Unix(int64(uTime), 0).Format("2006-01-02 15:04:05")
	fmt.Println("区块时间", blockT)
	fmt.Println("================================================")
	db.Create(&EthUsdc{
		NumberBloc: numberBlock,
		BlockHash:  blocHash.String(),
		Dire:       dire,
		Price:      priceS,
		Volume:     volumeS,
		Value:      valueS,
		Sender:     sender,
		Recipient:  recipient,
		TxIndex:    txIndex,
		TxHash:     txHash.String(),
		Timestamp:  uTime,
		DateTime:   blockT,
	})

}
func HandError(err error, msg string) {
	if err != nil {
		log.Fatalf("[error] %s :%s", msg, err)
	}
}

// FormatFloat 小树转化，保留n位小数
func FormatFloat(bigNum *big.Float, decimal int) string {
	num, _ := bigNum.Float64()
	// 默认乘1
	d := float64(1)
	if decimal > 0 {
		// 10的N次方
		d = math.Pow10(decimal)
	}
	return strconv.FormatFloat(math.Trunc(num*d)/d, 'f', -1, 64)
}
